#include "PluginMotorBike.h"
class IOConfigure;
#include <QDebug>
#include "mainWindow/mainWindow.h"
#include "ModelData/modelDataFactory.h"
#include "ModelData/modelDataSingleton.h"
#include "ModelData/modelDataBase.h"
#include "IO/IOConfig.h"
#include <QDir>
#include <QApplication>
#include <QFile>
#include <vtkSTLWriter.h>
#include <vtkSmartPointer.h>
#include <QTextCodec>
#include "meshData/meshSingleton.h"
#include "meshData/meshKernal.h"
#include <vtkDataSet.h>
#include <QTextStream>
#include <QTextCodec>
#include "IO/SolverIO.h"
#include <QtMath>
#include <QMessageBox>
namespace GUI
{
    class MainWindow;
}

namespace Plugins {
    PluginMotorBike::PluginMotorBike(GUI::MainWindow *m)
    {
        mainWindow = m;
        _describe = "MotorBike Plugin";
    }
bool Plugins::PluginMotorBike::install()
{
    // 注册写出的后缀与方法
    IO::IOConfigure::RegisterInputFile("OpenFoamRollDamp", inputFile);
    // 注册结果文件转换方法
    IO::IOConfigure::RegisterOutputTransfer("OpenFoamFileTrans", outputTransfer);

    return true;
}
bool PluginMotorBike::uninstall()
{
    // 移除写出文件的后缀注册
    IO::IOConfigure::RemoveInputFile("OpenFoamRollDamp");
    // 移除结果文件转换
    IO::IOConfigure::RemoveOutputTransfer("OpenFoamFileTrans");
    return true;
}
    GUI::MainWindow *PluginMotorBike::getMainWindow()
    {
        return mainWindow;
    }
}



void Register(GUI::MainWindow* m, QList<Plugins::PluginBase*>* ps)
{
    Plugins::PluginBase* p = new Plugins::PluginMotorBike(m);
    ps->append(p);
}

bool inputFile(QString path, ModelData::ModelDataBase *modelData)
{
    QString casePath = path + "/";
    QString resultPath = casePath + "Result/";
    QDir dir;
    // 创建结果文件夹
    if(!dir.exists(resultPath)){
        if(!dir.mkpath(resultPath)){
            sendMessageToConsole("Failed to create resultfolder!");
            return false;
        }
    }
    // 拷贝求解器
    /**
    if (!copyDirectoryFiles(QCoreApplication::applicationDirPath() + "/../solver/motorBike", casePath + "/motorBike", true))
    {
        sendMessageToConsole(QObject::tr("Failed to copy solver!"));
        return false;
    }**/
    // 生成字典参数文件
    if(!writeInputFile(QCoreApplication::applicationDirPath() + "/../solver/motorBike/system/initParameter", modelData)){
        sendMessageToConsole("Failed to write file initParameter!");
        return false;
    }else{
        return true;
    }
}

bool outputTransfer(QString path)
{
    QString solverPath = QCoreApplication::applicationDirPath() + "/../solver/motorBike";
    /*
    QFile file(QCoreApplication::applicationDirPath() + "/../solver/motorBike/system/initParameter");
    if(file.exists()){
        file.remove();
    }*/
    QStringList rowList;
    QString sourcePath = solverPath + "/postProcessing/cuttingPlane";
    QString destPath = path + "/Result/CuttingPlane/";
    if (!copyDirectoryFiles(sourcePath, destPath))
    {
        sendMessageToConsole(QStringLiteral("从PostProcessing/cuttingPlane目录复制文件到Result/cuttingPlane出错"));
        return false;
    }
    sourcePath = solverPath + "/postProcessing/sets/streamlines/";
    destPath = path + "/Result/streamlines/";
    if (!copyDirectoryFiles(sourcePath, destPath))
    {
        sendMessageToConsole(QStringLiteral("从PostProcessing/sets/streamlines目录复制文件到Result/cuttingPlane出错"));
        return false;
    }
    QString fileName = solverPath + "/postProcessing/forceCoeffs1/0/forceCoeffs.dat";
    QFile file_y(fileName);
    if (!file_y.exists())
    {
        sendMessageToConsole(QStringLiteral("postProcessing/forceCoeffs1/0/目录下，不存在coefficient.dat文件转换出错"));
        return false;
    }

    if (!file_y.open(QIODevice::ReadOnly))
        return false;
    rowList.clear();
    QString lineStr;
    while (!file_y.atEnd())
    {
        lineStr = file_y.readLine().trimmed();
        if (lineStr != "")
        {
            if (!lineStr.contains("# Time"))
            {
                continue;
            }
            lineStr = "Time       Cm           Cd           Cl           Cl(f)        Cl(r)";
            rowList.append(lineStr);
            break;
        }
    }
    while (!file_y.atEnd())
    {
        lineStr = file_y.readLine();
        lineStr = lineStr.replace("	", "   ");
        rowList.append(lineStr);
    }
    QString curFileName;
    curFileName = path + "/Result/coefficient.dat";
    QFile resultFile(curFileName);
    if (!resultFile.open(QIODevice::WriteOnly | QIODevice::Text))return false;
    QTextStream stream(&resultFile);
    for (int i = 0; i < rowList.count(); i++)
    {
        stream << rowList.at(i) << endl;
    }
    resultFile.close();
    return true;
}

bool copyDirectoryFiles(const QString &source, const QString &destination)
{
    QDir sourceDir(source);
    QDir destDir(destination);
    if(!sourceDir.exists()){
        return false;
    }else if(!destDir.exists()){
        if(!destDir.mkpath(destDir.absolutePath())){
            return false;
        }
    }
    QFileInfoList fileInfoList = sourceDir.entryInfoList();
    int fileIndex = 0;
    foreach (QFileInfo fileInfo, fileInfoList) {

        if(fileInfo.fileName() == "." || fileInfo.fileName() == ".."){
            continue;
        }else if(fileInfo.isDir()){
            if(!copyDirectoryFiles(fileInfo.filePath(), destDir.filePath(fileInfo.fileName()))){
                return false;
            }
        }else{
            if(destDir.exists(fileInfo.fileName())){
                destDir.remove(fileInfo.fileName());
            }
            QString destFileName = fileInfo.fileName();
            if (fileInfo.fileName().startsWith("p_y"))
            {
                destFileName = QString("%1%2%3").arg("p").arg(fileIndex).arg(".vtk");
                ++fileIndex;
            }else if (fileInfo.fileName().startsWith("track0_age_p"))
            {
                destFileName = QString("%1%2%3").arg("streamline").arg(fileIndex).arg(".vtk");
                ++fileIndex;
            }

            if(!QFile::copy(fileInfo.filePath(), destDir.filePath(destFileName))){
                return false;
            }
        }
    }
    return true;
}

void sendMessageToConsole(QString message)
{
    GUI::MainWindow* mainWindow = Plugins::PluginMotorBike::getMainWindow();
    if(mainWindow == nullptr){
        return;
    }
    ModuleBase::Message msg;
    msg.type = ModuleBase::MessageType::Warning_Message;
    msg.message = message;
    emit mainWindow->printMessageToMessageWindow(msg);
}

char* getParameterValue(QString name, ModelData::ModelDataBase *modelData)
{
    QString value = nullptr;
    DataProperty::ParameterBase *parameter = modelData->getParameterByName(name);
    if(parameter != nullptr){
        DataProperty::ParaType paraType = parameter->getParaType();
        switch (paraType) {
        case DataProperty::Para_Int:
        {
            DataProperty::ParameterInt* pInt=(DataProperty::ParameterInt*)(parameter);
            value = QString("%1").arg(pInt->getValue());
            break;
        }
        case DataProperty::Para_Bool:
        {
            DataProperty::ParameterBool* pBool = (DataProperty::ParameterBool*)(parameter);
            if (pBool->getValue())
            {
                value = QString("%1").arg("true");
            }
            else
            {
                value = QString("%1").arg("false");
            }
            break;
        }
        case DataProperty::Para_Double:
        {
            DataProperty::ParameterDouble* pDouble = (DataProperty::ParameterDouble*)(parameter);
            value = QString("%1").arg(pDouble->getValue());
            break;
        }
        case DataProperty::Para_String:
        {
            value = parameter->valueToString();
            break;
        }
        case DataProperty::Para_Selectable:
        {
            DataProperty::ParameterSelectable* pCombox = (DataProperty::ParameterSelectable*)(parameter);
            value = pCombox->getOption()[pCombox->getCurrentIndex()];
            break;
        }
        default:
            value = nullptr;
            break;
        }
    }
    if(value == nullptr){
        return nullptr;
    }else{
        return value.toLatin1().data();
    }
}

DataProperty::ParameterTable *getParameterTableValue(QString name, ModelData::ModelDataBase *modelData)
{
    DataProperty::ParameterBase* parameter = modelData->getParameterByName(name);
    if (parameter != nullptr && (parameter->getParaType() == DataProperty::Para_Table))
    {
        return (DataProperty::ParameterTable*)(parameter);
    }else{
        return nullptr;
    }
}


bool writeInputFile(QString filename, ModelData::ModelDataBase *modelData)
{
    QFile inputFile(filename);
    if(!inputFile.open(QIODevice::WriteOnly | QIODevice::Text)){
        return false;
    }
    QTextStream inputStream(&inputFile);

    // blockMeshDict需求的参数写入（流体域定义、各方向网格的数量）
    int rowCount = 0;
    int columnCount = 0;
    DataProperty::ParameterTable* domainVertices = getParameterTableValue("vertices", modelData);
    if( domainVertices == nullptr){
        sendMessageToConsole("Failed to get solve domain!");
        return false;
    }
    rowCount = domainVertices->getRowCount();
    columnCount = domainVertices->getColumnCount();
    if(rowCount != 8 || columnCount != 3){
        sendMessageToConsole("Failed to define solve domain!");
        return false;
    }
    double x, y, z;
    for(int rowIndex = 0; rowIndex < rowCount; ){
        x = domainVertices->getValue(rowIndex, 0);
        y = domainVertices->getValue(rowIndex, 1);
        z = domainVertices->getValue(rowIndex, 2);
        ++rowIndex;
        inputStream << QString("x%1\t%2;").arg(rowIndex).arg(x) << endl;
        inputStream << QString("y%1\t%2;").arg(rowIndex).arg(y) << endl;
        inputStream << QString("z%1\t%2;").arg(rowIndex).arg(z) << endl;
    }

    DataProperty::ParameterTable* numOfGridToEachEdge = getParameterTableValue("blocks", modelData);
    if( numOfGridToEachEdge == nullptr){
        sendMessageToConsole("Failed to get num of grid!");
        return false;
    }
    if(numOfGridToEachEdge->getRowCount() != 1 || numOfGridToEachEdge->getColumnCount() != 3){
        sendMessageToConsole("Failed to define num of grid!");
        return false;
    }
    inputStream << QString("xgridnumber\t%1;").arg(numOfGridToEachEdge->getValue(0, 0)) << endl;
    inputStream << QString("ygridnumber\t%1;").arg(numOfGridToEachEdge->getValue(0, 1)) << endl;
    inputStream << QString("zgridnumber\t%1;").arg(numOfGridToEachEdge->getValue(0, 2)) << endl;

    // decomposeParDict需求的参数写入（分解求解域的方法、各方向的分解数量）
    inputStream << QString("method\t%1;").arg(getParameterValue("method", modelData)) << endl;
    DataProperty::ParameterTable* numOfSubdomainsToEachEdge = getParameterTableValue("n", modelData);
    if(numOfSubdomainsToEachEdge == nullptr){
        sendMessageToConsole("Failed to get num of subdomains!");
        return false;
    }
    if(numOfSubdomainsToEachEdge->getRowCount() != 1 || numOfSubdomainsToEachEdge->getColumnCount() != 3){
        sendMessageToConsole("Failed to define num of subdomains!");
        return false;
    }
    int nx, ny, nz, numOfSubdomains;
    nx = numOfSubdomainsToEachEdge->getValue(0, 0);
    ny = numOfSubdomainsToEachEdge->getValue(0, 1);
    nz = numOfSubdomainsToEachEdge->getValue(0, 2);
    numOfSubdomains = QString(getParameterValue("numberOfSubdomains", modelData)).toInt();
    if(nx * ny * nz != numOfSubdomains){
        sendMessageToConsole("Failed to define subdomains!");
        return false;
    }
    inputStream << QString("nx\t%1;").arg(nx) << endl;
    inputStream << QString("ny\t%1;").arg(ny) << endl;
    inputStream << QString("nz\t%1;").arg(nz) << endl;

    inputStream << QString("numberOfSubdomains\t%1;").arg(numOfSubdomains) << endl;

    // snappyHexMeshDict需求的参数写入（是否切割网格、是否进行网格贴合、是否添加网格边界层、最大/小表面细化等级、边界层添加迭代数）
    inputStream << QString("castellatedMesh\t%1;").arg(getParameterValue("castellatedMesh", modelData)) << endl;
    inputStream << QString("snap\t%1;").arg(getParameterValue("snap", modelData)) << endl;
    inputStream << QString("addLayers\t%1;").arg(getParameterValue("addLayers", modelData)) << endl;
    DataProperty::ParameterTable* level = getParameterTableValue("level", modelData);
    if(level == nullptr){
        sendMessageToConsole("Failed to get level!");
        return false;
    }
    if(level->getRowCount() != 1 || level->getColumnCount() != 2){
        sendMessageToConsole("Failed to define level!");
        return false;
    }
    int firstLevel, secondLevel;
    firstLevel = QString("%1").arg(level->getValue(0, 0)).toInt();
    secondLevel = QString("%1").arg(level->getValue(0, 1)).toInt();
    if(firstLevel > secondLevel){
        inputStream << QString("minlevel\t%1;").arg(secondLevel) << endl;
        inputStream << QString("maxlevel\t%1;").arg(firstLevel) << endl;
    }else{
        inputStream << QString("minlevel\t%1;").arg(firstLevel) << endl;
        inputStream << QString("maxlevel\t%1;").arg(secondLevel) << endl;
    }
    inputStream << QString("nLayerIter\t%1;").arg(getParameterValue("nLayerIter", modelData)) << endl;

    // controlDict需求的参数写入（求解器名称、文件生成间隔、开始时间步、结束时间步）
    inputStream << QString("application\t%1;").arg(getParameterValue("application", modelData)) << endl;
    inputStream << QString("writeInterval\t%1;").arg(getParameterValue("writeInterval", modelData)) << endl;
    inputStream << QString("startTime\t%1;").arg(getParameterValue("startTime", modelData)) << endl;
    inputStream << QString("endTime\t%1;").arg(getParameterValue("endTime", modelData)) << endl;

    inputFile.close();
    return true;
}

